from marshmallow import Schema,fields,validate,validates,ValidationError,post_load,validates_schema
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field

from .models import User
from application import message,db,faker,redis


class MobileSchema(Schema):
    """手机号转换器"""
    mobile = fields.String(required=True, validate=validate.Regexp("^1[3-9]\d{9}$", error=message.mobile_format_error))

    @validates("mobile")
    def validate_mobile(self,data):
        """验证手机号是否已经被注册"""
        user = User.query.filter(User.mobile==data).first()
        if user:
            raise ValidationError(message=message.mobile_is_use)

        return data

class UserSchema(SQLAlchemyAutoSchema):
    mobile = auto_field(required=True, load_only=True)
    password = fields.String(required=True, load_only=True)
    password2 = fields.String(required=True, load_only=True)
    sms_code = fields.String(required=True, load_only=True)

    class Meta:
        model = User
        include_fk = True  # 启用外键关系
        include_relationships = True  # 模型关系外部属性
        fields = ["id", "name", "mobile", "password", "password2", "sms_code"]  # 如果要全换全部字段，就不要声明fields或exclude字段即可
        sqla_session = db.session

    @post_load()
    def save_object(self, data, **kwargs):
        """保存用户基本信息"""
        data.pop("password2")
        data.pop("sms_code")
        data["name"] = faker.name()
        instance = User(**data)
        db.session.add( instance )
        db.session.commit()
        return instance

    @validates_schema
    def validate(self, data, **kwargs):
        # 校验密码和确认密码
        if data["password"] != data["password2"]:
            raise ValidationError(message=message.password_not_match,field_name="password")

        # 1. 从redis中提取验证码
        redis_sms_code = redis.get("sms_%s" % data["mobile"])
        if redis_sms_code is None:
            raise ValidationError(message=message.sms_code_expired, field_name="sms_code")

        # python从redis中提取的数据最终都是bytes类型，所以需要进行编码处理
        redis_sms_code = redis_sms_code.decode()

        # 2. 从客户端提交的数据data中提取验证码
        sms_code = data["sms_code"]
        # 3. 字符串比较，如果失败，则抛出异常，否则，直接删除验证码
        if sms_code != redis_sms_code:
            raise ValidationError(message=message.sms_code_not_match, field_name="sms_code")

        redis.delete("sms_%s" % data["mobile"])

        return data